﻿using Sayook.Framework.DbCore.DbException;
using Sayook.Framework.DbCore.SqlModel;
using Sayook.Framework.Tools;
using System;
using System.Linq;
using System.Linq.Expressions;

namespace Sayook.Framework.DbCore
{
    /// <summary>
    /// 解析SQL表达式
    /// </summary>
    public static class ParseSqlExpression
    {
        /// <summary>
        /// 解析 SQL:SELECT子句表达式
        /// </summary>
        /// <typeparam name="T">数据库实体</typeparam>
        /// <param name="expression">表达式</param>
        /// <returns>SQL SELECT 子句</returns>
        public static string ParseSelectExpression<T, TElement>(this Expression<Func<T, TElement>> expression)
        {
            var fileds = ParseExpressionHelper.ParseExpression(expression);
            string selectFileds;
            if (fileds?.Any() != true)
            {
                selectFileds = SqlKeyWord.FiledAll;
            }
            else if (fileds.Count() == 1)
            {
                selectFileds = fileds.First();
            }
            else
            {
                selectFileds = string.Join(',', fileds);
            }
            return selectFileds;
        }

        /// <summary>
        /// 解析 SQL:SELECT DISTINCT子句表达式
        /// </summary>
        /// <typeparam name="T">数据库实体</typeparam>
        /// <typeparam name="TElement">查询字段的类型</typeparam>
        /// <param name="expression">查询字段表达式 eg:x => x.Name</param>
        /// <returns>SQL SELECT 子句</returns>
        public static string ParseSelectDistinctExpression<T, TElement>(this Expression<Func<T, TElement>> expression)
        {
            var fileds = ParseExpressionHelper.ParseExpression(expression);
            if (fileds?.Count() != 1)
            {
                throw new CreateSqlException("SELECT DISTINCT子句的查询字段必须为一个！eg:x => x.Name");
            }
            return fileds.First();
        }

        /// <summary>
        /// 解析SQL:WHERE子句表达式
        /// </summary>
        /// <typeparam name="T">数据库实体</typeparam>
        /// <param name="expression">条件字段表达式 eg:x => SqlWhere.Where(x.Id, SqlOperator.Equal)</param>
        /// <returns>"field = @field"</returns>
        public static string ParseWhereExpression<T>(this Expression<Func<T, SqlWhere>> expression)
        {
            var body = expression.Body as MethodCallExpression;

            //获取字段
            MemberExpression memberExpression = body.Arguments[0] as MemberExpression;
            string filed = memberExpression.Member.Name;

            //获取运算符号
            var operatorExp = body.Arguments[1] as ConstantExpression;
            var sqlOperator = (SqlOperator)operatorExp.Value;
            string _operator = sqlOperator.Description();

            if ((sqlOperator == SqlOperator.Like ||
                sqlOperator == SqlOperator.LeftLike ||
                sqlOperator == SqlOperator.RightLike ||
                sqlOperator == SqlOperator.In ||
                sqlOperator == SqlOperator.NotIn) &&
                body.Arguments.Count != 3)
            {
                throw new ParseExpressionException("调用 LIKE | IN 类型操作符时,SqlWhere.Set()务必传入3参数！");
            }

            if (sqlOperator == SqlOperator.Between &&
                body.Arguments.Count != 4)
            {
                throw new ParseExpressionException("调用 BETWEEN 操作符时,SqlWhere.Set()务必传入4参数！");
            }

            //获取条件值
            string value1 = null;
            string value2 = null;
            if (body.Arguments.Count > 2)
            {
                var arg = (T)Activator.CreateInstance(typeof(T));
                var where = expression.Compile().Invoke(arg);

                if (sqlOperator == SqlOperator.In ||
                sqlOperator == SqlOperator.NotIn)
                {
                    var value = where.Value1;
                    var type = value?.GetType();
                    if (value == null ||
                        !type.IsSerializable ||
                       (!type.IsArray &&
                        type.FullName.IndexOf("List") < 0)
                        )
                    {
                        throw new ParseExpressionException("调用 IN | NOT IN 操作符时,SqlWhere.Set()务必传入有效数组或集合！");
                    }

                    var json = JsonHelper.ToJson(value);
                    value1 = json.Replace("\"", "").Replace("[", "").Replace("]", "").Replace(",", "','");
                }
                else
                {
                    value1 = where.Value1?.ToString();
                    value2 = where.Value2?.ToString();
                }
            }

            switch (sqlOperator)
            {
                case SqlOperator.Equal:
                case SqlOperator.Unqual:
                case SqlOperator.Great:
                case SqlOperator.Less:
                case SqlOperator.GreatOrEqual:
                case SqlOperator.LessOrEqual:
                    {
                        if (string.IsNullOrWhiteSpace(value1))
                        {
                            value1 = $"@{filed}";
                        }
                        else
                        {
                            value1 = $"'{value1}'";
                        }
                        return string.Format(_operator, filed, value1);
                    }

                case SqlOperator.Like:
                case SqlOperator.RightLike:
                case SqlOperator.LeftLike:
                    {
                        if (string.IsNullOrWhiteSpace(value1))
                        {
                            throw new ParseExpressionException("调用 LIKE 类型操作符时,SqlWhere.Set()务必传入有效参数[NotNull & NonEmpty]！");
                        }
                        return string.Format(_operator, filed, value1);
                    }

                case SqlOperator.Between:
                    {
                        if (string.IsNullOrWhiteSpace(value1) ||
                            string.IsNullOrWhiteSpace(value2))
                        {
                            throw new ParseExpressionException("调用 BETWEEN 操作符时,SqlWhere.Set()务必传入有效参数[NotNull & NonEmpty]！");
                        }
                        return string.Format(_operator, filed, value1, value2);
                    }

                case SqlOperator.In:
                case SqlOperator.NotIn:
                    return string.Format(_operator, filed, value1);

                default: return string.Empty;
            }
        }

        /// <summary>
        /// 解析SQL:ORDER BY子句表达式
        /// </summary>
        /// <typeparam name="T">数据库实体</typeparam>
        /// <typeparam name="TElement">排序字段类型</typeparam>
        /// <param name="expression">表达式</param>
        /// <returns>"字段名"</returns>
        public static string ParseOrderbyExpression<T, TElement>(this Expression<Func<T, TElement>> expression)
        {
            var fileds = ParseExpressionHelper.ParseExpression(expression);
            if (fileds?.Any() != true)
            {
                throw new ParseExpressionException("解析SQL:ORDER BY子句表达式 表达式错误或为空");
            }
            return fileds.First();
        }

        /// <summary>
        /// 解析SQL:INSERT子句表达式
        /// </summary>
        /// <typeparam name="T">数据库模型类</typeparam>
        /// <param name="expression">表达式</param>
        /// <returns>INSERT 字句：(新增字段,字段值)</returns>
        public static (string, string) ParseInsertExpression<T, TElement>(this Expression<Func<T, TElement>> expression)
        {
            var fileds = ParseExpressionHelper.ParseExpression(expression);
            if (fileds?.Any() != true)
            {
                fileds = ReflectionHelper.GetPublicPropertiesByFilterCustomAttributes<T>(typeof(AutoIncrementAttribute));
            }
            return (string.Join(',', fileds), $"@{string.Join($",@", fileds)}");
        }

        /// <summary>
        ///  解析SQL:UPDATE子句表达式
        /// </summary>
        /// <typeparam name="T">数据库模型类</typeparam>
        /// <typeparam name="TElement"></typeparam>
        /// <param name="expression">表达式</param>
        /// <returns>"filed1 = @filed1 , filed2 = @filed2"</returns>
        public static string ParseUpdateExpression<T, TElement>(this Expression<Func<T, TElement>> expression)
        {
            var set = string.Empty;
            var fileds = ParseExpressionHelper.ParseExpression(expression);
            if (fileds?.Any() != true)
            {
                throw new ParseExpressionException("解析SQL:UPDATE子句表达式=>更新字段格式错误");
            }
            else if (fileds.Count() == 1)
            {
                var filed = fileds.First();
                set = $"{filed} = @{filed}";
            }
            else
            {
                foreach (var filed in fileds)
                {
                    set += $"{filed} = @{filed},";
                }
                set = set.Substring(startIndex: 0, set.Length - 1);
            }

            return set;
        }
    }
}